﻿/* Modificado por Sergio. 15/06/10. Agregadas propiedades para cambiar valores de la proyeccion de la camara
 *                                  Hecho mas eficiente la actualizacion de la proyeccion de la camara (para 
 *                                  que solo se haga cuando sus valores fueron cambiados realmente, sino no)
 * Modificado por Sergio. 24/06/10. Corregidas cosas de la projection para que sea mas eficiente
 * Modificado por Sergio. 25/06/10. Agregadas propiedades estaticas para poder tener muchas camaras
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Engine
{
    public class Camera
    {
        static List<Camera> mCameraList = new List<Camera>();
        static int mActiveCamera = -1;
        int mCameraNumber = -1;

        Matrix mViewMatrix; // View Matrix
        Matrix mProjectionMatrix; // Projection Matrix

        // Camera Transform (LookAt Values)
        protected Transform mTransform = new Transform();

        // Camera Projection Values
        bool mProjectionChanged = false;
        float mFieldOfView = 0;
        float mAspectRatio = 0;
        float mNearPlaneDistance = 0;
        float mFarPlaneDistance = 0;

        #region Constructores

        public Camera(Viewport viewport)
        {
            mCameraList.Add(this);
            mCameraNumber = mCameraList.Count - 1;
            mActiveCamera = mCameraNumber;

            mTransform = new Transform();

            mAspectRatio = ((float)viewport.Width) / ((float)viewport.Height);
            mFieldOfView = 60.0f;
            mNearPlaneDistance = 1.0f;
            mFarPlaneDistance = 10000.0f;
            mProjectionChanged = true;
        }
        public Camera(Viewport viewport, Vector3 pos, Vector3 lookAt)
        {
            mCameraList.Add(this);
            mCameraNumber = mCameraList.Count - 1;
            mActiveCamera = mCameraNumber;

            mTransform = new Transform(pos);
            mTransform.LookAt(lookAt);

            mAspectRatio = ((float)viewport.Width) / ((float)viewport.Height);
            mFieldOfView = 60.0f;
            mNearPlaneDistance = 1.0f;
            mFarPlaneDistance = 1000000.0f;
            mProjectionChanged = true;
        }

        #endregion

        public void Update()
        {
            if (mActiveCamera == mCameraNumber)
            {
                mViewMatrix = Matrix.CreateLookAt(mTransform.Position,
                                                  mTransform.Position + mTransform.Forward,
                                                  mTransform.Up);

                if (mProjectionChanged) // Actualizar la matriz de proyeccion solo si hubo cambios en sus valores
                {
                    mProjectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(mFieldOfView),
                                                                            mAspectRatio,
                                                                            mNearPlaneDistance,
                                                                            mFarPlaneDistance);
                    mProjectionChanged = false;
                }
            }
        }

        #region SetsAndGets

        public static int ActiveCameraNumber
        {
            get { return mActiveCamera; }
            set
            {
                if (value > mCameraList.Count - 1 || value < 0)
                    mActiveCamera = mCameraList.Count - 1;
                else
                    mActiveCamera = value;
            }
        }

        public static Camera ActiveCamera
        {
            get
            {
                if (mCameraList.Count > 0)
                    return mCameraList[mActiveCamera];
                return null;
            }
        }

        public int CameraNumber
        {
            get { return this.mCameraNumber; }
        }
       
        public static Matrix ViewMatrix
        {
            get
            {
                if (mCameraList.Count > 0)
                {
                    return mCameraList[mActiveCamera].mViewMatrix;
                }
                return Matrix.CreateLookAt(Vector3.Zero, Vector3.Forward, Vector3.Up);
            }
        }
        public static Matrix ProjectionMatrix
        {
            get
            {
                if (mCameraList.Count > 0)
                {
                    return mCameraList[mActiveCamera].mProjectionMatrix;
                }
                return Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(60),1,1,1000);
            }
        }

        public Transform Transform
        {
            get { return this.mTransform; }
            set { this.mTransform = value; }
        }

        public float FieldOfView
        {
            get { return this.mFieldOfView; }
            set
            {
                if (value != this.mFieldOfView)
                {
                    this.mFieldOfView = value;
                    mProjectionChanged = true;
                }
            }
        }

        public float AspectRatio
        {
            get { return this.mAspectRatio; }
            set
            {
                if (value != this.mAspectRatio)
                {
                    this.mAspectRatio = value;
                    mProjectionChanged = true;
                }
            }
        }

        public float NearPlaneDistance
        {
            get { return this.mNearPlaneDistance; }
            set
            {
                if (value != mNearPlaneDistance)
                {
                    this.mNearPlaneDistance = value;
                    mProjectionChanged = true;
                }
            }
        }

        public float FarPlaneDistance
        {
            get { return this.mFarPlaneDistance; }
            set
            {
                if (value != mFarPlaneDistance)
                {
                    this.mFarPlaneDistance = value;
                    mProjectionChanged = true;
                }
            }
        }

        #endregion

    }
}
